local function has_words_before()
  local line, col = unpack(vim.api.nvim_win_get_cursor(0))
  return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match "%s" == nil
end

local function mapping(is_cmdline)
  if is_cmdline == nil then is_cmdline = false end
  local cmp = require "cmp"
  local luasnip = require "luasnip"

  return {
    ["<CR>"] = cmp.config.disable,
    -- ["<C-z>"] = {
    --   c = function()
    --     if cmp.visible() then
    --       cmp.select_next_item()
    --     else
    --       cmp.complete()
    --     end
    --   end,
    -- },
    -- ["<Tab>"] = cmp.mapping(function(fallback)
    --   if is_cmdline then
    --     if cmp.visible() then
    --       cmp.confirm()
    --     else
    --       fallback()
    --     end
    --   else
    --     if cmp.visible() and has_words_before() then
    --       cmp.confirm { select = true }
    --     else
    --       fallback()
    --     end
    --   end
    -- end, { "i", "c" }),
    -- ["<S-Tab>"] = {
    --   c = function()
    --     if cmp.visible() then
    --       cmp.select_prev_item()
    --     else
    --       cmp.complete()
    --     end
    --   end,
    -- },
    -- ["<C-n>"] = cmp.mapping(function()
    --   if luasnip.jumpable(1) then luasnip.jump(1) end
    -- end, { "i" }),
    -- ["<C-p>"] = cmp.mapping(function()
    --   if luasnip.jumpable(-1) then luasnip.jump(-1) end
    -- end, { "i" }),
    -- ["<C-e>"] = {
    --   c = cmp.mapping.abort(),
    -- },
    -- ["<C-y>"] = {
    --   c = cmp.mapping.confirm { select = false },
    -- },
    -- ["<C-k>"] = cmp.mapping(function() cmp.select_prev_item { behavior = cmp.SelectBehavior.Select } end, { "i", "c" }),
    -- ["<C-j>"] = cmp.mapping(function()
    --   if cmp.visible() then
    --     cmp.select_next_item { behavior = cmp.SelectBehavior.Select }
    --   else
    --     cmp.complete()
    --   end
    -- end, { "i", "c" }),
  }
end

local source_names = {
  nvim_lsp = "(LSP)",
  emoji = "(Emoji)",
  path = "(Path)",
  calc = "(Calc)",
  cmp_tabnine = "(Tabnine)",
  vsnip = "(Snippet)",
  luasnip = "(Snippet)",
  buffer = "(Buffer)",
  tmux = "(TMUX)",
  copilot = "(Copilot)",
  treesitter = "(TreeSitter)",
  cmdline = "(Cmdline)",
  vim_dadbod_completion = "(DADBOD)",
}

return {
  "hrsh7th/nvim-cmp",
  dependencies = {
    "AstroNvim/astrolsp",
    {
      "hrsh7th/cmp-cmdline",
      keys = { ":", "/", "?" }, -- lazy load cmp on more keys along with insert mode
      dependencies = { "hrsh7th/nvim-cmp" },
      opts = function()
        local cmp = require "cmp"
        return {
          {
            -- completion = {
            --   completeopt = "menu,menuone,noselect",
            -- },
            type = { "/", "?" },
            -- mapping = mapping(),
            sources = {
              { name = "buffer" },
            },
          },
          {
            -- completion = {
            --   completeopt = "menu,menuone,noselect",
            -- },
            type = ":",
            -- mapping = mapping(true),
            sources = cmp.config.sources {
              { name = "path" },
              {
                name = "cmdline",
                option = {
                  ignore_cmds = { "Man", "!" },
                },
              },
            },
          },
        }
      end,
      config = function(_, opts)
        local cmp = require "cmp"
        vim.tbl_map(function(val) cmp.setup.cmdline(val.type, val) end, opts)
      end,
    },
  },
  opts = function(_, opts)
    local cmp = require "cmp"
    local lspkind = require "lspkind"

    return require("astrocore").extend_tbl(opts, {
      experimental = { ghost_text = true },
      completion = {
        -- 自动选中第一条
        completeopt = "menu,menuone,noinsert",
      },
      sources = cmp.config.sources {
        { name = "nvim_lsp", priority = 1000 },
        { name = "luasnip", priority = 900 },
        { name = "path", priority = 750 },
        { name = "buffer", priority = 250 },
      },
      mapping = mapping(),
      formatting = {
        format = lspkind.cmp_format {
          with_text = true, -- do not show text alongside icons
          before = function(entry, vim_item)
            -- Source 显示提示来源
            -- vim_item.menu = "[" .. string.upper(entry.source.name) .. "]"
            vim_item.menu = source_names[entry.source.name]
            return vim_item
          end,
        },
      },
    })
  end,
}
